package utils

import (
	"errors"
	"fmt"
	"go-http/netdisk/conf"
	"time"

	jwt "github.com/dgrijalva/jwt-go"
)

// type StandardClaims struct {
// 	Audience  string `json:"aud,omitempty"`	// 受众
// 	ExpiresAt int64  `json:"exp,omitempty"`	// 过期时间
// 	Id        string `json:"jti,omitempty"`	// jwt的唯一身份标识，主要用来作为一次性token,从而回避重放攻击。
// 	IssuedAt  int64  `json:"iat,omitempty"`	// 发布时间
// 	Issuer    string `json:"iss,omitempty"`	// 发行者
// 	NotBefore int64  `json:"nbf,omitempty"`	// 生效时间
// 	Subject   string `json:"sub,omitempty"`	// 主题
// }

// type Token struct {
// 	Raw       string                 // The raw token.  Populated when you Parse a token
// 	Method    SigningMethod          // The signing method used or to be used
// 	Header    map[string]interface{} // The first segment of the token
// 	Claims    Claims                 // The second segment of the token
// 	Signature string                 // The third segment of the token.  Populated when you Parse a token
// 	Valid     bool                   // Is the token valid?  Populated when you Parse/Verify a token
// }

type CustomClaims struct {
	/***************** 属性名首字母必须大写 ****************/
	// User     string `json:"username"`
	Uid string `json:"uid"`
	// IsAdmin  uint8  `json:"isadmin"`
	// Usertype uint8  `json:"usertype"`
	jwt.StandardClaims
}

func GenerateToken(uid string) (string, error) {
	claims := &CustomClaims{
		uid,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(conf.TOKEN_EXPIRES * time.Second).Unix(),
			Id:        NewV4String(),
			Issuer:    "eyebluetank",
		},
	}

	// fmt.Printf("claims:%#v\n", claims)

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(conf.TOKEN_SIGNKEY)

	return tokenString, err
}

func ParseToken(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return conf.TOKEN_SIGNKEY, nil
	})

	if err != nil {
		return nil, err
	}

	if _, ok := token.Claims.(*CustomClaims); !ok || !token.Valid {
		msg := fmt.Sprintf("ok: %t, valid:%t", ok, token.Valid)
		return nil, errors.New(msg)
	}

	return token.Claims.(*CustomClaims), nil
}

func ParseTokenIgnorExpired(tokenString string) (*CustomClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return conf.TOKEN_SIGNKEY, nil
	})

	if e, _ := err.(*jwt.ValidationError); e.Errors != jwt.ValidationErrorExpired {
		return nil, err
	}

	if _, ok := token.Claims.(*CustomClaims); !ok {
		msg := fmt.Sprintf("token:%#v", token)
		return nil, errors.New(msg)
	}

	return token.Claims.(*CustomClaims), nil
}
